package com.jamal2362.apktool.task;

import android.content.Context;
import android.content.res.AssetManager;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
import org.json.JSONException;
import org.json.JSONObject;
import com.jamal2362.apktool.R;
import com.jamal2362.apktool.fragment.files.Refreshable;
import com.jamal2362.apktool.util.FileUtils;
import com.jamal2362.apktool.util.TextUtil;
import com.jamal2362.apktool.util.as.ASUtils;
import com.jamal2362.apktool.util.as.JsonMerger;
import com.jamal2362.apktool.util.search.SearchUtils;
import com.jamal2362.apktool.util.Apkinfo;

public class AntiSplit extends AbstractTask {
	private Context context;
	private File tempDir;
	private Refreshable refresh;

	public AntiSplit(Context ctx, Refreshable refresh) {
		super(ctx, refresh);
		this.context = ctx;
		this.refresh = refresh;
	}

	@Override
	protected int getTitle() {
		return R.string.mmerge;
	}

	@Override
	protected boolean process(File f) {
		info(R.string.text, context.getString(R.string.mmerge));
		try {
			FileFilter dirFilter = new FileFilter() {
				@Override
				public boolean accept(File file) {
					return file.isDirectory();
				}
			};
			File[] dirs = f.listFiles(dirFilter);
			File general = null;
			ArrayList<File> containsPublic = new ArrayList<File>();
			for (File d : dirs) {
				File pub = new File(d.getAbsolutePath() + "/res/values/public.xml");
				if (checkGeneral(d)) {
					general = d;
					info(R.string.mbase_dir, general.getName());
				} else if (pub.exists()) {
					containsPublic.add(d);
				}
			}
			if (general == null) {
				error(R.string.text, "Base directory not found, exit!!!");
				return false;
			}
			this.tempDir = general;
			final File manifest = new File(general.getAbsolutePath() + "/AndroidManifest.xml");
			mergeJson(general, dirs);
			for (File dir : containsPublic) {
				searchDummy(dir, general);
			}
			for (File dir : containsPublic) {
				searchDummy(general, dir);
			}
			cleanPublic(general);
			Apkinfo appInfo = new Apkinfo(context, general.getParent() + "/" + general.getName() + ".apk");
			String outApk = "out.apk";
			if (appInfo.isValid()) {
				outApk = TextUtil.replaceIllegal(appInfo.label() + "_v" + appInfo.version() + "(" + appInfo.code() + ").apk");
			}
			try {
				File ajson = new File(general.getAbsolutePath() + "/apktool.json");
				JSONObject json = JsonMerger.fromFile(ajson);
				JSONObject ins = new JSONObject();
				json.put("apkFileName", outApk);
				if (!json.isNull("unknownFiles"))
					ins = (JSONObject) json.get("unknownFiles");
				ins.put("ANTISPLIT.txt", "8");
				json.put("unknownFiles", ins);
				AssetManager asset = context.getAssets();
				File unknow = new File(general.getAbsolutePath() + "/unknown");
				if (!unknow.exists())
					unknow.mkdir();
				File insert = new File(unknow, "ANTISPLIT.txt");
				InputStream as_in = asset.open("asinsert");
				OutputStream as_out = new FileOutputStream(insert);
				IOUtils.copy(as_in, as_out);
				as_in.close();
				as_out.close();
				JsonMerger.writeToFile(ajson, json);
			} catch (Exception e) {
				warning(R.string.text, e.toString());
			}
			String splitName = ASUtils.findSplitName(manifest);
			if (splitName != null) {
				new File(general.getAbsolutePath() + "/res/xml/" + splitName + ".xml").delete();
				File pf = new File(general.getAbsolutePath() + "/res/values/public.xml");
				String publ = ASUtils.readToString(pf);
				publ = ASUtils.replaceReg(publ, "\\s+<public type=\"xml\" name=\"" + splitName + "\" id=\"[^\"]+\" />", "");
				ASUtils.writeToFile(pf, publ);
			}
			clearManifest(manifest);
			copyFiles(general, dirs);
			return true;
		} catch (Exception e) {
			error(R.string.errorf, e.toString());
			return false;
		}
	}

	@Override
	protected boolean shouldShowFinishDialog() {
		return false;
	}

	@Override
	protected void onPostExecute(Boolean result) {
		super.onPostExecute(result);
		SignUtil.loadKey(context, new SignUtil.LoadKeyCallback(){
				@Override
				public void call(SignUtil signTool) {
					BuildTask bt = new BuildTask(context, refresh, signTool);
					bt.antiSplit(true);
					bt.execute(tempDir);
				}
			});
	}

	public boolean checkGeneral(File dir) {
		File manifest = new File(dir.getAbsolutePath() + "/AndroidManifest.xml");
		File dex = new File(dir.getAbsolutePath() + "/classes.dex");
		if (!manifest.exists() || !dex.exists())
			return false;
		String fstr = ASUtils.readToString(manifest);
		Pattern pattern1 = Pattern.compile("\\s+configForSplit=\"[^\"\\s]+\"");
		Matcher matcher1 = pattern1.matcher(fstr);
		if (matcher1.find()) {
			return false;
		}
		Pattern pattern2 = Pattern.compile("\\s+android:isFeatureSplit=\"true\"");
		Matcher matcher2 = pattern2.matcher(fstr);
		if (matcher2.find()) {
			return false;
		}
		return true;
	}

	public void searchDummy(File dir, File source) {
		File pub = new File(dir.getAbsolutePath() + "/res/values/public.xml");
		HashMap<String, String> pubids = ASUtils.findIds(pub);
		if (pubids.size() == 0)
			return;
		info(R.string.mprepare, (Object) null);
		File pubSource = new File(source.getAbsolutePath() + "/res/values/public.xml");
		File manifest = new File(dir.getAbsolutePath() + "/AndroidManifest.xml");
		File resDir = new File(dir.getAbsolutePath() + "/res");
		HashMap<String, String> array = ASUtils.arrayIds(pubSource, pubids);
		int count = 0;
		info(R.string.mprocessing, dir.getName());
		FileFilter dirFilter = new FileFilter() {
			@Override
			public boolean accept(File file) {
				return file.isDirectory();
			}
		};
		FilenameFilter xmlFilter = new FilenameFilter() {
			@Override
			public boolean accept(File file, String p2) {
				return p2.endsWith(".xml");
			}
		};
		File[] dirs = resDir.listFiles(dirFilter);
		for (File d : dirs) {
			File[] files = d.listFiles(xmlFilter);
			if (files == null)
				continue;
			for (File f : files) {
				if (!SearchUtils.indexOf(f, "APKTOOL_DUMMY", false))
					continue;
				String fstr = ASUtils.readToString(f);
				//fstr = ASUtils.replaceReg(fstr, "\\s+<item[^>]+name=\"APKTOOL_DUMMY_[^\"]+\">false</item>", "");
				for (Map.Entry<String, String> entry : pubids.entrySet()) {
					String dummy = entry.getKey();
					String[] duarr = dummy.split(";");
					String dummyName = array.get(dummy);
					if (dummyName == null)
						continue;
					fstr = ASUtils.replacer(fstr, duarr, dummyName);
				}
				ASUtils.writeToFile(f, fstr);
				count++;
			}
		}
		if (manifest.exists()) {
			if (SearchUtils.indexOf(manifest, "APKTOOL_DUMMY", false)) {
				String fstr = ASUtils.readToString(manifest);
				for (Map.Entry<String, String> entry : pubids.entrySet()) {
					String dummy = entry.getKey();
					String[] duarr = dummy.split(";");
					String dummyName = array.get(dummy);
					if (dummyName == null)
						continue;
					fstr = ASUtils.replacer(fstr, duarr, dummyName);
				}
				ASUtils.writeToFile(manifest, fstr);
				count++;
			}
		}
		info(R.string.mprocessed, count);
	}

	public void cleanPublic(File source) {
		File f = new File(source.getAbsolutePath() + "/res/values/public.xml");
		if (!SearchUtils.indexOf(f, "APKTOOL_DUMMY", false))
			return;
		String fstr = ASUtils.readToString(f);
		fstr = ASUtils.replaceReg(fstr, "\\s+<[^>]+name=\"APKTOOL_DUMMY_[^\"]+\"[^>]+/>", "");
		ASUtils.writeToFile(f, fstr);
	}

	public void clearManifest(File manifest) {
		info(R.string.mcleaning_manifest, (Object) null);
		String mf = ASUtils.readToString(manifest);
		mf = ASUtils.replaceReg(mf, "\\s+android:extractNativeLibs=\"(false|true)\"", "");
        mf = ASUtils.replaceReg(mf, "\\s+android:isSplitRequired=\"(false|true)\"", "");
		mf = ASUtils.replaceReg(mf, "\\s+<meta-data\\s+android:name=\"com\\.android\\.vending\\.splits\\.required\"\\s+android:value=\"(false|true)\"\\s+/>", "");
		mf = ASUtils.replaceReg(mf, "\\s+<meta-data\\s+android:name=\"com\\.android\\.vending\\.splits\"\\s+android:value=\"@xml/splits\\d+\"\\s+/>", "");
		mf = ASUtils.replaceReg(mf, "\\s+<meta-data\\s+android:name=\"com\\.android\\.vending\\.derived\\.apk\\.id\"\\s+android:value=\"\\d+\"\\s+/>", "");
		ASUtils.writeToFile(manifest, mf);
	}

	public void mergeJson(File general, File[] dirs) {
		info(R.string.mmerge_json, (Object) null);
		File gjson = new File(general.getAbsolutePath() + "/apktool.json");
		JSONObject go = JsonMerger.fromFile(gjson);
		JSONObject res = null;
		for (File d : dirs) {
			if (!d.equals(general)) {
				File js = new File(d.getAbsolutePath() + "/apktool.json");
				if (!js.exists())
					continue;
				JSONObject gd = JsonMerger.fromFile(js);
				try {
					res = JsonMerger.jsonMerge(go, gd);
				} catch (JSONException e) {
					continue;
				}
			}
		}
		JsonMerger.writeToFile(gjson, res);
	}

	public void copyDex(File general, File dir) {
		FilenameFilter dexFilter = new FilenameFilter() {
			@Override
			public boolean accept(File file, String p2) {
				return p2.startsWith("classes") && p2.endsWith(".dex");
			}
		};
		File[] files = dir.listFiles(dexFilter);
		if (files == null)
			return;
		for (File dex : files) {
			File cdex = new File(general.getAbsolutePath() + "/" + dex.getName());
			if (cdex.exists()) {
				String name = genDexName(general.getAbsolutePath(), "classes", ".dex", 2);
				cdex = new File(general.getAbsolutePath() + "/" + name);
			}
			FileUtils.copyFiles(dex, cdex);
		}
	}

	public String genDexName(String path, String name, String suff, int cnt) {
		try {
			String tn = name;
			if (cnt > 0) {
				tn = name + cnt;
			}
			File check = new File(path, tn + suff);
			if (check.exists()) {
				return genDexName(path, name, suff, ++cnt);
			} else {
				return tn + suff;
			}
		} catch (Exception e) {
			e.printStackTrace();
			return name + suff;
		}
	}

	public void copyFiles(File general, File[] dirs) {
		FilenameFilter dirFilter = new FilenameFilter() {
			@Override
			public boolean accept(File file, String p2) {
				return file.isDirectory();
			}
		};
		for (File d : dirs) {
			if (d.equals(general))
				continue;
			// copyDex(general, d);
			File[] files = d.listFiles(dirFilter);
			if (files == null)
				continue;
			for (File f : files) {
				if (!f.getName().equals("original")) {
					try {
						File out = new File(general.getAbsolutePath() + "/" + f.getName());
						if (!out.exists())
							out.mkdir();
						FileUtils.cpdir(f, out);
					} catch (Exception e) {
						continue;
					}
				}
			}
		}
	}
}
